-
Notifications
You must be signed in to change notification settings - Fork 0
토큰 재발급 시 JWT 필터를 거쳐 오류가 발생하는 문제 수정 #110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughHTTP 보안 설정과 JWT 인증 필터에서 회원 인증 및 이메일 관련 엔드포인트를 명시적으로 인증 없이 접근 가능하도록 변경했습니다. 이와 함께 이력서 서비스에서 이미지 URL 처리 로직을 단순화했으며, 리소스 프로퍼티의 서브프로젝트 커밋 참조가 갱신되었습니다. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java(1 hunks)src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java(2 hunks)src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java(1 hunks)src/main/resources/properties(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
⚙️ CodeRabbit Configuration File
**/*.java: This project follows key rules from the NAVER Campus Hackday Java Coding Conventions (partial summary below; see full spec for details). * Indentation: use spaces (no tabs), tab width = 4 spaces. Files must end with Unix LF newline. * Line length: maximum 120 characters. * Imports: single-class imports only, allow wildcard for static imports, group imports with blank lines between sections. * Assignment operators (=,+=,-=, etc.): always have one space before and after; when breaking lines, the operator must start the next line (Binary expressions: operation sign on next line). * Lambda expressions: omit parentheses for a single parameter; surround->with spaces (param -> expression); use braces and explicitreturnfor multi-statement bodies; choose short, clear parameter names to keep intent visible. For the complete set of conventions and detailed examples, please refer to the full spec athttps://naver.github.io/hackday-conventions-java/.
Files:
src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.javasrc/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.javasrc/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
🧠 Learnings (3)
📓 Common learnings
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/domain/AuthCode.java:21-32
Timestamp: 2025-05-09T13:04:36.837Z
Learning: AuthCode 엔티티에 있는 만료된 레코드(expired_at이 경과된 레코드)를 삭제하는 작업은 다음 PR에서 구현될 예정입니다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (2)
Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.
Learnt from: patulus
PR: #32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:64-67
Timestamp: 2025-05-12T01:00:17.875Z
Learning: AuthEmailService는 회원 검증과 이메일 인증 관련 비즈니스 예외(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID)를 발생시키며, 이 예외들은 호출 서비스로 전파된다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (1)
Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.
🔇 Additional comments (3)
src/main/resources/properties (1)
1-1: 서브모듈 커밋 업데이트 확인 필요서브프로젝트 커밋 해시만 변경되었습니다. 로컬 및 CI 환경 모두에서
git submodule update --init --recursive커맨드로 실제 서브모듈이 해당 커밋으로 체크아웃되는지 확인해 주세요. 누락되면 빌드가 실패할 수 있습니다.src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (1)
54-57: 토큰 재발급 시 JWT 필터 오류 수정을 위한 적절한 보안 설정토큰 재발급(
/api/v1/members/refresh)을 포함하여 인증이 필요하지 않은 엔드포인트들을 명시적으로permitAll()로 설정한 것이 올바릅니다. 이는 PR의 주요 목적인 "토큰 재발급 시 JWT 필터를 거쳐 오류가 발생하는 문제"를 해결하는 핵심적인 변경사항입니다.
/api/v1/members/sign-in: 로그인 엔드포인트/api/v1/members/sign-up/**: 회원가입 관련 엔드포인트/api/v1/members/refresh: 토큰 재발급 엔드포인트 (핵심)/api/v1/email/**: 이메일 인증 관련 엔드포인트src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (1)
36-37: JWT 필터 제외 로직이 올바르게 구현됨
shouldNotFilter메서드의 로직이 올바르게 수정되었습니다. Authorization 헤더가 없거나 제외 경로에 해당하는 요청은 JWT 필터를 건너뛰도록 하여, SecurityConfig의 설정과 일치하게 동작합니다. 이는 토큰 재발급 시 JWT 필터 오류를 해결하는 핵심 로직입니다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
Outdated
Show resolved
Hide resolved
src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java(1 hunks)src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java(2 hunks)src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java(1 hunks)src/main/resources/properties(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java
⚙️ CodeRabbit Configuration File
**/*.java: This project follows key rules from the NAVER Campus Hackday Java Coding Conventions (partial summary below; see full spec for details). * Indentation: use spaces (no tabs), tab width = 4 spaces. Files must end with Unix LF newline. * Line length: maximum 120 characters. * Imports: single-class imports only, allow wildcard for static imports, group imports with blank lines between sections. * Assignment operators (=,+=,-=, etc.): always have one space before and after; when breaking lines, the operator must start the next line (Binary expressions: operation sign on next line). * Lambda expressions: omit parentheses for a single parameter; surround->with spaces (param -> expression); use braces and explicitreturnfor multi-statement bodies; choose short, clear parameter names to keep intent visible. For the complete set of conventions and detailed examples, please refer to the full spec athttps://naver.github.io/hackday-conventions-java/.
Files:
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.javasrc/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.javasrc/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
🧠 Learnings (3)
📓 Common learnings
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/domain/AuthCode.java:21-32
Timestamp: 2025-05-09T13:04:36.837Z
Learning: AuthCode 엔티티에 있는 만료된 레코드(expired_at이 경과된 레코드)를 삭제하는 작업은 다음 PR에서 구현될 예정입니다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (2)
Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.
Learnt from: patulus
PR: #32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:64-67
Timestamp: 2025-05-12T01:00:17.875Z
Learning: AuthEmailService는 회원 검증과 이메일 인증 관련 비즈니스 예외(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID)를 발생시키며, 이 예외들은 호출 서비스로 전파된다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (1)
Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.
🔇 Additional comments (2)
src/main/resources/properties (1)
1-1: 서브모듈 커밋 SHA 갱신 시 파이프라인 정상 동작 검증 권장
properties파일의 서브모듈 커밋 해시가11b82a2…로 변경되었습니다. 실제 서브모듈 디렉터리가 이 SHA로 체크아웃되어 있고 CI/CD 파이프라인에서git submodule update --init --recursive과정이 정상 동작하는지 확인해 주세요. 해시 불일치 시 빌드 실패가 발생할 수 있습니다.src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (1)
54-57: LGTM! 토큰 재발급 관련 엔드포인트 접근 권한이 적절히 설정되었습니다.회원 인증 관련 엔드포인트들(
sign-in,sign-up,refresh)과 이메일 관련 엔드포인트를permitAll()로 설정한 것은 올바른 접근입니다. 이러한 엔드포인트들은 본질적으로 인증이 필요하지 않은 공개 API이므로 보안상 문제가 없습니다.
|
|
||
| @Override | ||
| protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { | ||
| String[] excludePath = {"api/v1/members/sign-in", "api/v1/members/sign-up/**", "/api/v1/members/refresh", "/api/v1/email/**"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
경로 일관성 문제를 수정하세요.
excludePath 배열에서 경로의 일관성이 맞지 않습니다. 일부 경로는 "/" 접두사가 있고 일부는 없습니다.
다음과 같이 모든 경로에 "/" 접두사를 추가하세요:
- String[] excludePath = {"api/v1/members/sign-in", "api/v1/members/sign-up/**", "/api/v1/members/refresh", "/api/v1/email/**"};
+ String[] excludePath = {"/api/v1/members/sign-in", "/api/v1/members/sign-up", "/api/v1/members/refresh", "/api/v1/email"};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
at line 33, the excludePath array has inconsistent path prefixes, with some
paths starting with "/" and others not. To fix this, ensure all paths in the
excludePath array consistently start with a "/" prefix by adding it to those
missing it.
| return authorizationHeader == null | ||
| || Arrays.stream(excludePath).anyMatch(request.getRequestURI()::startsWith); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
startsWith 사용으로 인한 보안 취약점을 수정하세요.
현재 startsWith 방식은 의도하지 않은 경로까지 매치할 수 있는 보안 문제가 있습니다. 예를 들어 "/api/v1/members/sign-in-test" 같은 경로도 매치됩니다.
정확한 패턴 매칭을 위해 다음과 같이 수정하세요:
- return authorizationHeader == null
- || Arrays.stream(excludePath).anyMatch(request.getRequestURI()::startsWith);
+ String requestURI = request.getRequestURI();
+ return authorizationHeader == null
+ || requestURI.equals("/api/v1/members/sign-in")
+ || requestURI.startsWith("/api/v1/members/sign-up/")
+ || requestURI.equals("/api/v1/members/refresh")
+ || requestURI.startsWith("/api/v1/email/");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return authorizationHeader == null | |
| || Arrays.stream(excludePath).anyMatch(request.getRequestURI()::startsWith); | |
| String requestURI = request.getRequestURI(); | |
| return authorizationHeader == null | |
| || requestURI.equals("/api/v1/members/sign-in") | |
| || requestURI.startsWith("/api/v1/members/sign-up/") | |
| || requestURI.equals("/api/v1/members/refresh") | |
| || requestURI.startsWith("/api/v1/email/"); |
🤖 Prompt for AI Agents
In
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
around lines 36 to 37, the use of startsWith for path matching causes security
issues by matching unintended paths. Replace the startsWith check with an exact
match or a more precise pattern matching method to ensure only the intended
paths in excludePath are matched, preventing accidental matches like
"/api/v1/members/sign-in-test".
| Resume resume = resumeRepository.findById(resumeId) | ||
| .orElseThrow(()-> new BusinessException(ExceptionType.RESUME_NOT_FOUND)); | ||
|
|
||
| String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
권한 체크 후에 이미지 URL을 생성하세요.
권한이 확인되지 않은 상태에서 resumeImageService.getResumeImagePresignedUrl(memberId)를 호출하는 것은 보안상 좋지 않습니다. 해당 사용자가 실제로 이력서의 소유자인지 확인한 후에 이미지 URL을 생성해야 합니다.
다음과 같이 순서를 변경하세요:
@Transactional
public ResumeResponse updateResume(ResumeRequest request, Long memberId, Long resumeId) {
Resume resume = resumeRepository.findById(resumeId)
.orElseThrow(()-> new BusinessException(ExceptionType.RESUME_NOT_FOUND));
- String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);
if(!resume.getMember().getId().equals(memberId)) {
throw new BusinessException(ExceptionType.RESUME_FORBIDDEN);
}
+
+ String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId); | |
| @Transactional | |
| public ResumeResponse updateResume(ResumeRequest request, Long memberId, Long resumeId) { | |
| Resume resume = resumeRepository.findById(resumeId) | |
| .orElseThrow(() -> new BusinessException(ExceptionType.RESUME_NOT_FOUND)); | |
| if (!resume.getMember().getId().equals(memberId)) { | |
| throw new BusinessException(ExceptionType.RESUME_FORBIDDEN); | |
| } | |
| String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId); | |
| // ... the rest of the method ... | |
| } |
🤖 Prompt for AI Agents
In src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
at line 112, the code calls
resumeImageService.getResumeImagePresignedUrl(memberId) before verifying the
user's authorization. To fix this, first perform a permission check to confirm
that the user is the owner of the resume, and only after successful
authorization, call getResumeImagePresignedUrl to generate the image URL. This
ensures secure access control before generating sensitive URLs.
What is this PR?🔍
Changes💻
Summary by CodeRabbit
신규 기능
버그 수정
기타